#include "armor.h"

#include <cmath>

using namespace cv;
using namespace std;

static void adjustArmorRect(cv::RotatedRect &rect);


Armor::Armor() = default;
Armor::~Armor() = default;

void Armor::preventExceed(int &x, int &y, int &width, int &height, const cv::Mat &src)
{
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x + width > src.cols) width = src.cols - x;
    if (y + height > src.rows) height = src.rows - y;
}

void Armor::assignMemberVariables(const Lightbar &lbar, const Lightbar &rbar)
{
    float delta_width = fabs(rbar.rect.center.x - lbar.rect.center.x);
    float delta_height = fabs(rbar.rect.center.y - lbar.rect.center.y);
    double armor_width = sqrt(delta_width * delta_width + delta_height * delta_height);
    double armor_height = (lbar.rect.size.height + rbar.rect.size.height) / 2.0;
    double armor_ratio = armor_width / armor_height;
    double armor_bar_len_ratio = lbar.rect.size.height / rbar.rect.size.height;
    armor_bar_len_ratio = ((armor_bar_len_ratio > 1.0) ? armor_bar_len_ratio : (1.0 / armor_bar_len_ratio));

    this->lbar_rect = lbar.rect;
    this->rbar_rect = rbar.rect;
    std::vector<cv::Point> armor_vertices;
    extractPoints(this->lbar_rect, armor_vertices);
    extractPoints(this->rbar_rect, armor_vertices);
    this->armor_rect = cv::minAreaRect(armor_vertices);
    adjustArmorRect(armor_rect);
    this->armor_color = rbar.color;
    this->ratio = armor_ratio;
    this->bar_len_ratio = armor_bar_len_ratio;
    this->bar_delta_angle = abs(lbar.angle - rbar.angle);
    this->armor_angle = fabs(atan2(delta_height, delta_width) * 180 / M_PI);
}

void Armor::assignMemberVariables(const array<Point2f, 4> &coordinates)
{
    Point2f left_center = {(coordinates[0].x + coordinates[1].x) / 2, (coordinates[0].y + coordinates[1].y) / 2};
    Point2f right_center = {(coordinates[2].x + coordinates[3].x) / 2, (coordinates[2].y + coordinates[3].y) / 2};
    Point2f top_center = {(coordinates[0].x + coordinates[3].x) / 2, (coordinates[0].y + coordinates[3].y) / 2};
    Point2f bottom_center = {(coordinates[2].x + coordinates[1].x) / 2, (coordinates[2].y + coordinates[1].y) / 2};
    double armor_width = std::abs(right_center.x - left_center.x);
    double armor_height = std::abs(bottom_center.y - top_center.y);
    double armor_ratio = armor_width / armor_height;
    double armor_bar_len_ratio = (coordinates[1].y - coordinates[0].y) / (coordinates[2].y - coordinates[3].y);
    armor_bar_len_ratio = ((armor_bar_len_ratio > 1.0) ? armor_bar_len_ratio : (1.0 / armor_bar_len_ratio));
    this->armor_rect = cv::minAreaRect(coordinates);
    adjustArmorRect(armor_rect);
    this->ratio = armor_ratio;
    this->bar_len_ratio = armor_bar_len_ratio;
    this->bar_delta_angle = std::abs(atan2(coordinates[1].y - coordinates[0].y, coordinates[1].x - coordinates[0].x) -
                                     atan2(coordinates[2].y - coordinates[3].y, coordinates[2].x - coordinates[3].x));
    this->armor_angle = std::abs(atan2(left_center.y - right_center.y, left_center.x - right_center.x) * 180 / M_PI);
}

static void adjustArmorRect(cv::RotatedRect &rect)
{
    if (rect.size.width < rect.size.height) rect = cv::RotatedRect(rect.center, cv::Size2f(rect.size.height, rect.size.width), rect.angle + 90);
}

void Armor::listInfo() const
{
    std::cout << "amor_rect_area: " << this->armor_rect.size.area() << std::endl;
    std::cout << "armor_ratio: " << this->ratio << std::endl;
    std::cout << "bar_delta_angle; " << this->bar_delta_angle << std::endl;
    std::cout << "armor_angle: " << this->armor_angle << std::endl;
}

void Armor::extractPoints(const cv::RotatedRect &rect, std::vector<cv::Point> &points)
{
    cv::Point2f mid_points[4];
    rect.points(mid_points);
    for (auto & mid_point : mid_points) { points.emplace_back(std::move(mid_point)); }
}

void Armor::setNumberImg(const Mat &img)
{
    cv::Rect2d rect_left = lbar_rect.boundingRect();
    cv::Rect2d rect_right = rbar_rect.boundingRect();
    int min_x, min_y, max_x, max_y;
    // 左右的x坐标取二八分界点作为灯条和数字的分割线
    min_x = int(fmin(rect_left.x + 0.8 * rect_left.width, rect_right.x + 0.8 * rect_right.width));
    max_x = int(fmax(rect_left.x + 0.2 * rect_left.width, rect_right.x + 0.2 * rect_right.width));
    min_y = int(fmin(rect_left.y, rect_right.y) - 0.5 * (rect_left.height + rect_right.height) / 2.0);
    max_y = int(fmax(rect_left.y + rect_left.height, rect_right.y + rect_right.height) +
                0.5 * (rect_left.height + rect_right.height) / 2.0);

    int temp_width = max_x - min_x;
    int temp_height = max_y - min_y;
    preventExceed(min_x, min_y, temp_width, temp_height, img);
    number_img = img(cv::Rect(min_x, min_y, temp_width, temp_height));
    resize(number_img, number_img, cv::Size(48, 36), INTER_AREA);
    cvtColor(number_img, number_img, COLOR_BGR2GRAY);
}
